#!/usr/bin/env python
#-*- coding: utf-8 -*-


import inspect
# import cPickle as pickle
import _pickle as pickle

from redis import StrictRedis

__all__ = ('RedisDB', )


def python_get(self, key):
    value = self.get(key)
    return pickle.loads(value) if value else value


def python_set(self, key, value, timeout=None):
    value = pickle.dumps(value)
    if not timeout:
        return self.set(key, value)
    else:
        return self.setex(key, timeout, value)


StrictRedis.python_get = python_get
StrictRedis.python_set = python_set


class RedisDB(object):

    """
    Simple as dead support of Redis database for Flask apps.
    """

    def __init__(self, app=None, config_prefix=None):
        """
        If app argument provided then initialize redis connection using
        application config values.
        If no app argument provided you should do initialization later with
        :meth:`init_app` method.
        :param app: Flask application instance.
        :param config_prefix: Config prefix to use.
        """
        if app is not None:
            self.init_app(app, config_prefix)

    def init_app(self, app, config_prefix=None):
        """
        Actual method to read redis settings from app configuration,
        initialize redis connection and copy all public connection methods to
        current instance.
        :param app: Flask application instance.
        :param config_prefix: Config prefix to use. By default: 'REDIS'
        """
        # Put redis to application extensions
        if 'redis' not in app.extensions:
            app.extensions['redis'] = {}

        # Which config prefix to use, custom or default one?
        self.config_prefix = config_prefix = config_prefix or 'REDIS'

        # No way to do registration two times
        if config_prefix in app.extensions['redis']:
            raise ValueError('Already registered config prefix {0!r}.'.
                             format(config_prefix))

        # Start reading configuration, define converters to use and key func
        # to prepend config prefix to key value
        converters = {'port': int}

        convert = lambda arg, value: (
            converters[arg](value) if arg in converters else value
        )

        key = lambda suffix: '{0}_{1}'.format(config_prefix, suffix)

        klass = StrictRedis

        # Read connection args spec, exclude self from list of possible
        args = inspect.getfullargspec(klass.__init__).args
        args.remove('self')

        # Prepare keyword arguments
        kwargs = dict([(
            arg, convert(
                arg, app.config[key(arg.upper())]
            ))
            for arg in args if key(arg.upper()) in app.config
        ])

        # Initialize connection and store it to extensions
        self.connection = connection = klass(**kwargs)
        app.extensions['redis'][config_prefix] = connection

        # Include public methods to current instance
        self._include_public_methods(connection)

    def _include_public_methods(self, connection):
        """
        Include public methods from connection instance to current instance.
        """
        for attr in dir(connection):
            value = getattr(connection, attr)
            if attr.startswith('_') or not callable(value):
                continue
            self.__dict__[attr] = value
